import {BaseEle, createUUID, utils} from "./core.js";
import {runExpr} from "./jexpr.js";
import {parseEachCond} from "./frm.js";
import {arrayDiffById, eql} from "./ext.js";

export class ZEachBase extends BaseEle {
    constructor() {
        super();
        let self = this;
        self.itemsExpr = self.getAttribute("cond");
        // console.dir(self.$configEle)
        // self.$ctx = null
        self._shadowRoot = this
    }
    _initChildNodes() {
        // this.attachShadow({mode: 'open'});
        // this._shadowRoot = this.shadowRoot
        // this._shadowRoot.appendChild(this.$configEle.getTplBefore())
        //
        // let childNodes =  [...this.children].map(v => v)
        // this._insertBefore(childNodes);


        //
        // let slot = document.createElement("slot");
        // slot.setAttribute('cond', '')
        // slot.setAttribute('name', 'cond1')
        // this._shadowRoot.appendChild(slot);

        // this._insertAfter(childNodes);

        this.$templateArr = [...this.children].filter((v) =>{
            return !v.hasAttribute("default");
        })
        this.$templateArr.forEach(item => {
            hiddendiv.append(item)
        });
        let arr = [...this.children].filter((v) =>{
            return v.hasAttribute("default");
        });
        if (arr.length > 0) {
            this.$defaultTpl = arr[0]
            hiddendiv.append(this.$defaultTpl);
        }

    }
    _insertBefore(childNodes = []) {
        let nodes = []
        for (let child of childNodes) {
            if (child.nodeName === 'TEMPLATE') {
                break;
            }
            else {
                nodes.push(child)
            }
        }
        nodes.forEach(node => {
            this._shadowRoot.appendChild(node);
        })
    }
    _insertAfter(childNodes = []) {
        let startFetch = false;
        for (let child of childNodes) {
            if (child.nodeName === 'TEMPLATE') {
                startFetch = true;
            }
            if (startFetch) {
                if (child.nodeName !== 'TEMPLATE') {
                    this._shadowRoot.appendChild(child.cloneNode(true));
                }
            }
        }
    }
    notify() {
    }
    _getCondNodes() {
        // let slotnodes = [... this._shadowRoot.querySelectorAll('slot[cond]')].map(v => {
        //     return v.assignedNodes()
        // }).filter(v => v && v.length > 0);
        // let nodes = []
        // if (slotnodes.length > 0) {
        //     nodes = slotnodes[0]
        // }
        // return nodes
        return this.$templateArr
    }
    getConds() {
        let nodes = this._getCondNodes();
        return nodes.map(v => v.getAttribute("cond"));
    }
    clear() {
        let canClear = true;
        [...this._shadowRoot.childNodes].forEach(node => {
            if (canClear) {
                node?.remove()
            }
            // if (node.nodeName === 'SLOT' && node.hasAttribute("cond")) {
            //     canClear = true;
            // }
        })
    }
    getSubItem(index =0) {
        let canClearIndex = 0;
        let childNodes =[...this._shadowRoot.children]
        return childNodes[index]
        // for (let i = 0; i < childNodes.length; i++) {
        //     let node = childNodes[i];
        //     if (canClearIndex > 0) {
        //         if (index === i - canClearIndex - 1) {
        //             return node;
        //         }
        //     }
        //     if (node.nodeName === 'SLOT' && node.hasAttribute("cond")) {
        //         canClearIndex = i;
        //     }
        // }
    }
}

function travelSubApp(dom, ctx = {}) {
    if (!dom.$subApp) {
        dom.$subApp = ctx.ele
    }
    if (dom.children) {
        [...dom.children].forEach(child => {
            travelSubApp(child, ctx);
        })
    }
}

let hiddendiv = document.getElementById('zeachcon_fix')
if (!hiddendiv) {
    let cacheSet = new Set();
    hiddendiv = document.createElement("div");
    hiddendiv.setAttribute("id", "zeachcon_fix");
    hiddendiv.setAttribute("hidden", "true");

    hiddendiv.add =function (ele) {
        if (!cacheSet.has(ele)) {
            cacheSet.add(ele);
            hiddendiv.append(ele)
        }
    }
    document.body.append(hiddendiv);
}


class ZEach extends ZEachBase {
    constructor() {
        super();
        let self = this;
        self.itemsExpr = self.getAttribute("cond");
        self.eachBlockValueName = '';

        self._shadowRoot = this;
        this._initChildNodes();
        this._oldvalue = void 0
        // this.parentZEach = this.parentElement.closest('z-each')
        this._log('zeach ')
    }
    mounted() {
        requestAnimationFrame(() => {
            if (this.$ctx) {
                console.log('z-each mounted', this.$ctx?.getMethods(), this)
                this.$ctx?.initShadow(this._shadowRoot);
                this.notify()
            }
        })
    }
    unmounted() {
        this.$ctx.unRegister(this)
    }
    getConds() {
        let {ruleConds, byCondName, eachBlockValueName, eachBlockIndexName} = parseEachCond(this.itemsExpr)
        return [ruleConds[0]]
    }
    _append(nodes = [], index, onBuild) {
        let {ruleConds, byCondName, eachBlockValueName, eachBlockIndexName} = parseEachCond(this.itemsExpr)
        this.eachBlockValueName = eachBlockValueName;
        let arr = runExpr(ruleConds[0], {
            ...this.$ctx.getData()
        })

        if (Array.isArray(this._oldvalue)) {
            if (eql(this._oldvalue, arr)) {
                this._oldvalue = [...arr];
                return;
            }
            // let arrDiffResult = arrayDiffById(this._oldvalue, arr);
            // console.log('webcom arrDiffResult', this._oldvalue, arr, arrDiffResult.changed);
            //
            //
            // if (!arrDiffResult.changed) {
            //     this._oldvalue = [...arr];
            //     return;
            // }
        }
        if (Array.isArray(arr)) {
            this._oldvalue = [...arr];
        }

        this._log('webcom _append dom')


        // if ( ruleConds[0] === 'item.items' && arr.length < 1) {
        //     setInterval(() => {
        //         this._log('webcom append', ruleConds[0], this.$ctx.getData())
        //     }, 6000)
        // }
        let truearr = arr;

        let handleOrder = this.getAttribute('handleOrder');
        if (handleOrder) {
            let methods = this.$ctx.getMethods();
            if (methods[handleOrder]) {
                // truearr = structuredClone(truearr)
                // console.log('webcom handleOrder', handleOrder, methods[handleOrder]);
                truearr = methods[handleOrder](truearr);
            }
        }

        if (index > -1) {
            truearr = [arr[index]]
        }
        this._log('webcom append', arr, ruleConds)
        if (Array.isArray(truearr)) {
            for (let i = 0, len =  truearr.length; i < len; ++i) {
                let contentDoc = nodes[0].content.cloneNode(true)
                let doms = [...contentDoc.childNodes];
                let CommentbyName = byCondName ? byCondName :  'id';
                let comment = document.createComment('/subitem ' + ( truearr[i][CommentbyName] ? truearr[i][CommentbyName] : ''));
                doms.push(comment)
                // this._log(doms)
                let subData = {
                    [eachBlockValueName]: truearr[i],
                }
                if (eachBlockIndexName) {
                    if (byCondName) {
                        subData[eachBlockIndexName] = arr.findIndex(v => v[byCondName] === truearr[i][byCondName])
                    }
                    else {
                        subData[eachBlockIndexName] = arr.findIndex(v => v === truearr[i])
                    }
                }
                let subApp = this.$ctx.createSubApp(subData)
                subApp.mount(contentDoc);
                doms.filter(v => v instanceof Element).forEach(v => {
                    v.$subApp = subApp
                    let findCtx = {
                        ele: subApp
                    }
                    travelSubApp(v, findCtx);
                });

                if (onBuild) {
                    onBuild(doms)
                }
            }
            requestAnimationFrame(() => {
                this.$ctx.register(this, {
                    isEach: true
                })
            })
        }
    }
    notify({newvalue, index= -1, key = '', keys = [], isSubItem = false} = {}) {



        if (this._locks) {
            return
        }
        this.setLock()
        // let comments = utils.getAllComments(this._shadowRoot).filter(v => v.textContent.includes('/subitem'))
        let comments = this._shadowRoot.children
        this._log('webcom z-each notify',  comments.length, index)
        let nodes =  this._getCondNodes()
        this._log('webcom z-each notify', this, this.$ctx?.getDomMap())
        if (index < 0) {
            if (Array.isArray(nodes)) {
                this.clear()
                if (nodes[0]?.content && this.$ctx) {
                    this._append(nodes, index, (doms) => {
                        this._log('webcom append doms', doms)
                        doms.forEach(dom => {
                            this._shadowRoot.appendChild(dom);
                        })
                    })
                }
            }
        }
        else if (index > comments.length - 1) {
            this._log('webcom append after last')
            this._append(nodes, index, (doms) => {
                doms.forEach(dom => {
                    this._shadowRoot.appendChild(dom);
                })
            })
        }
        else {
            let subItem = this.getSubItem(index);
            if (isSubItem) {
                this._log('webcom update subitem', subItem, index)
                if (subItem) {
                    this._append(nodes, index, (doms) => {
                        doms.forEach(dom => {
                            subItem.after(dom)
                        })
                        subItem.remove()
                    })
                }
            }
            if (!isSubItem) {
                this._log('webcom update subitem key', subItem, index)
            //     let index = keys.findIndex(v => v === key);
            //     if (index > -1) {
            //         let subkeys = keys.slice(index +2, keys.length );
            //         if (subkeys.length > 0) {
            //             let subpath = [this.eachBlockValueName].concat(subkeys).join('.')
            //             // this._log(subpath, subItem.$subApp)
            //             subItem?.$subApp?.updateContent(subpath)
            //         }
            //     }
            }
        }

    }
}

ZEach.defsel('z-each',{
    attrs: {
        value: ''
    }
})